package com.bjy.qa.service.user.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.bjy.qa.dao.manage.ProjectDao;
import com.bjy.qa.dao.manage.RoleDao;
import com.bjy.qa.dao.user.UserCompanyProjectDao;
import com.bjy.qa.dao.user.UserDao;
import com.bjy.qa.dao.user.UserRoleDao;
import com.bjy.qa.entity.MyPage;
import com.bjy.qa.entity.ResponsePagingData;
import com.bjy.qa.entity.manage.Company;
import com.bjy.qa.entity.manage.Project;
import com.bjy.qa.entity.manage.Role;
import com.bjy.qa.entity.user.*;
import com.bjy.qa.exception.MyException;
import com.bjy.qa.service.user.IUserCompanyProjectService;
import com.bjy.qa.service.user.IUserRoleService;
import com.bjy.qa.service.user.IUserService;
import com.bjy.qa.util.jwt.JWTUtils;
import com.bjy.qa.util.redis.RedisUtil;
import com.bjy.qa.util.security.HmacUtil;
import com.bjy.qa.util.security.LoginUser;
import com.bjy.qa.util.security.SecurityUtils;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.ldap.core.LdapTemplate;
import org.springframework.ldap.filter.AndFilter;
import org.springframework.ldap.filter.EqualsFilter;
import org.springframework.ldap.query.LdapQuery;
import org.springframework.ldap.query.LdapQueryBuilder;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

@Service
public class UserService implements IUserService {
    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    @Resource
    UserDao userDao;

    @Resource
    UserCompanyProjectDao userCompanyProjectDao;

    @Resource
    IUserCompanyProjectService iUserCompanyProjectService;

    @Value("${qa-platform.secret-key}")
    String secretKey;

    @Value("${qa-platform.user.ldap.enable}")
    private boolean ldapEnable;

    @Value("${qa-platform.user.ldap.user-id}")
    private String userId;

    @Value("${qa-platform.user.ldap.user-base-dn}")
    private String userBaseDN;

    @Value("${qa-platform.user.ldap.object-class}")
    private String objectClass;

    @Value("#{'${qa-platform.user.default_role}'.trim().split(',')}")
    private List<String> defaultRole;

    @Value("#{'${qa-platform.user.default_project}'.trim().split(',')}")
    private List<String> defaultProject;

    @Autowired
    @Lazy
    private LdapTemplate ldapTemplate;

    @Resource
    private AuthenticationManager authenticationManager;

    @Resource
    UserRoleDao userRoleDao;

    @Resource
    ProjectDao projectDao;

    @Resource
    IUserRoleService iUserRoleService;

    @Resource
    RedisUtil redisUtil;

    @Resource
    RoleDao roleDao;

    @Override
    public UserResponse login(User inUser) {
        User user = null;
        if (ldapEnable && !inUser.getLocal()) {
            if (checkLdapAuthenticate(inUser)) {
                QueryWrapper<User> queryWrapper = new QueryWrapper<>();
                queryWrapper.eq("account", inUser.getAccount());
                user = userDao.selectOne(queryWrapper); // 查找用户信息
                if (user == null) {
                    User newUser = new User();
                    newUser.setAccount(inUser.getAccount());
                    newUser.setName(queryLdapUser(inUser.getAccount()).getCommonName());
                    newUser.setCode("ldap");
                    newUser.setLocked(false);
                    newUser.setCompanyId(0);
                    newUser.setProjectId(0);
                    newUser.setAvatar("https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif");
                    userDao.insert(newUser);

                    // 批量新增 用户-公司-项目 对应数据
                    List userCompanyProjectList = new LinkedList<UserCompanyProject>(); // 用户-公司-项目对应 list
                    for (String projectId : defaultProject) {
                        if (StringUtils.isNotBlank(projectId)) {
                            Project projectTmp = projectDao.selectById(Long.valueOf(projectId.trim()));
                            if (projectTmp != null) {
                                // 生成临时 UserCompanyProject 对象并添加到 userCompanyProjectList
                                UserCompanyProject userCompanyProjectTmp = new UserCompanyProject();
                                userCompanyProjectTmp.setUserId(newUser.getId());
                                userCompanyProjectTmp.setCompanyId(projectTmp.getCompanyId());
                                userCompanyProjectTmp.setProjectId(projectTmp.getId());
                                userCompanyProjectList.add(userCompanyProjectTmp);
                            }
                        }
                    }
                    iUserCompanyProjectService.saveBatch(userCompanyProjectList); // 批量新增 用户-公司-项目 对应数据

                    // 批量新增 用户-角色 对应数据
                    List userRoleList = new LinkedList<UserRole>(); // 用户-角色 对应 list
                    for (String roleId : defaultRole) {
                        if (StringUtils.isNotBlank(roleId)) {
                            // 生成临时 UserRole 对象并添加到 userRoleList
                            UserRole userRoleTmp = new UserRole();
                            userRoleTmp.setUserId(newUser.getId());
                            userRoleTmp.setRoleId(Long.valueOf(roleId.trim()));
                            userRoleList.add(userRoleTmp);
                        }
                    }
                    iUserRoleService.saveBatch(userRoleList); // 批量新增 用户-角色 对应数据

                    user = userDao.selectOne(queryWrapper); // 查找用户信息
                }
            }
        } else {
            String hmacCode = HmacUtil.encrypt(inUser.getAccount() + inUser.getCode(), secretKey, HmacUtil.HMAC_MD5); // 生成code的md5
            user = userDao.selectByAcountAndCode(inUser.getAccount(), hmacCode); // 查找用户信息
        }

        if (user != null) {
            // 使用 UsernamePasswordAuthenticationToken 认证（由于账号密码上边已经验证过了，所以这里就设置正确的密码）
            UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(user.getAccount(), user.getCode());
            Authentication authentication = authenticationManager.authenticate(authenticationToken); // 认证
            // 如果认证没通过，给出错误提示
            if (authentication == null) {
                throw new RuntimeException("登录失败，用户名或密码错误!");
            }

            String token = JWTUtils.getToken(user);

            UserResponse userResponse = new UserResponse();
            userResponse.setToken(token);
            return userResponse;
        } else {
            throw new MyException("登录失败，用户名或密码错误！");
        }
    }

    /**
     * ldap 验证
     * @param user 用户信息
     * @return
     */
    private boolean checkLdapAuthenticate(User user) {
        String account = user.getAccount();
        String code = user.getCode();
        logger.info("login check content username {}", account);
        AndFilter filter = new AndFilter();
        filter.and(new EqualsFilter("objectclass", objectClass)).and(new EqualsFilter(userId, account));
        try {
            boolean authResult = ldapTemplate.authenticate(userBaseDN, filter.toString(), code);
            return authResult;
        } catch (Exception e) {
            logger.error("ldap login failed, cause: {}", e.getMessage());
            return false;
        }
    }

    /**
     * 从 ldap 中查询用户信息
     * @param account 账号
     * @return
     */
    private Person queryLdapUser(String account) {
        logger.info("query ldap account {}", account);
        AndFilter filter = new AndFilter();
        filter.and(new EqualsFilter("objectclass", objectClass)).and(new EqualsFilter(userId, account));
        LdapQuery query = LdapQueryBuilder.query().base(userBaseDN).filter(filter.toString());
        try {
            return ldapTemplate.findOne(query, Person.class);
        } catch (Exception e) {
            logger.error("ldap query failed, cause: {}", e.getMessage());
            throw new MyException("LDAP 中未查查到该账号: " + account);
        }
    }

    @Override
    public User getInfo(Long id) {
        return userDao.selectById(id);
    }

    @Override
    public int add(User user) {
        if (userDao.selectByAcount(user.getAccount()) != null) {
            throw new MyException("用户创建失败，账号已存在！");
        }

        String hmacCode = HmacUtil.encrypt(user.getAccount() + user.getCode(), secretKey, HmacUtil.HMAC_MD5); // 生成code的md5
        user.setCode(hmacCode);

        int userInsertRet = userDao.insert(user); // 新增用户信息

        // 批量新增 用户-公司-项目 对应数据
        List userCompanyProjectList = new LinkedList<UserCompanyProject>(); // 用户-公司-项目对应 list
        for (Company companyTmp : user.getCompanyList()) { // 遍历所有公司
            for (Project projectTmp: companyTmp.getProjectList()) { // 遍历某个公司下的所有项目
                // 生成临时 UserCompanyProject 对象并添加到 userCompanyProjectList
                UserCompanyProject userCompanyProjectTmp = new UserCompanyProject();
                userCompanyProjectTmp.setUserId(user.getId());
                userCompanyProjectTmp.setCompanyId(companyTmp.getId());
                userCompanyProjectTmp.setProjectId(projectTmp.getId());
                userCompanyProjectList.add(userCompanyProjectTmp);
            }
        }
        iUserCompanyProjectService.saveBatch(userCompanyProjectList); // 批量新增 用户-公司-项目 对应数据

        // 删除 用户-角色 对应数据
        QueryWrapper<UserRole> queryUserRoleWrapper = new QueryWrapper<>();
        queryUserRoleWrapper.eq("user_id", user.getId());
        userRoleDao.delete(queryUserRoleWrapper);

        // 批量新增 用户-角色 对应数据
        List userRoleList = new LinkedList<UserRole>(); // 用户-角色 对应 list
        for (Object roleId : user.getRoles()) { // 遍历所有角色
            // 生成临时 UserRole 对象并添加到 userRoleList
            UserRole userRoleTmp = new UserRole();
            userRoleTmp.setUserId(user.getId());
            userRoleTmp.setRoleId(Long.valueOf(roleId.toString()));
            userRoleList.add(userRoleTmp);
        }
        iUserRoleService.saveBatch(userRoleList); // 批量新增 用户-角色 对应数据

        return userInsertRet;
    }

    @Override
    public int update(User user) {
        User oldUser = userDao.selectByAcount(user.getAccount());
        if (oldUser != null && oldUser.getId() != user.getId()) {
            throw new MyException("用户修改失败，账号已存在！");
        }

        // 更新数据需要清除 UpdatedTime 和 CreatedTime，否则不会自动更新修改时间
        user.setUpdatedAt(null);
        user.setCreatedAt(null);

        int userUpdateRet = userDao.updateById(user); // 更新用户信息

        // 删除 用户-公司-项目 对应数据
        QueryWrapper<UserCompanyProject> queryUserCompanyProjectWrapper = new QueryWrapper<>();
        queryUserCompanyProjectWrapper.eq("user_id", user.getId());
        userCompanyProjectDao.delete(queryUserCompanyProjectWrapper);

        // 批量新增 用户-公司-项目 对应数据
        List userCompanyProjectList = new LinkedList<UserCompanyProject>(); // 用户-公司-项目对应 list
        for (Company companyTmp : user.getCompanyList()) { // 遍历所有公司
            for (Project projectTmp: companyTmp.getProjectList()) { // 遍历某个公司下的所有项目
                // 生成临时 UserCompanyProject 对象并添加到 userCompanyProjectList
                UserCompanyProject userCompanyProjectTmp = new UserCompanyProject();
                userCompanyProjectTmp.setUserId(user.getId());
                userCompanyProjectTmp.setCompanyId(companyTmp.getId());
                userCompanyProjectTmp.setProjectId(projectTmp.getId());
                userCompanyProjectList.add(userCompanyProjectTmp);
            }
        }
        iUserCompanyProjectService.saveBatch(userCompanyProjectList); // 批量新增 用户-公司-项目 对应数据

        // 删除 用户-角色 对应数据
        QueryWrapper<UserRole> queryUserRoleWrapper = new QueryWrapper<>();
        queryUserRoleWrapper.eq("user_id", user.getId());
        userRoleDao.delete(queryUserRoleWrapper);

        // 批量新增 用户-角色 对应数据
        List<String> userRoleNameList = new ArrayList<>(); // 用户-角色名称 list
        List userRoleList = new LinkedList<UserRole>(); // 用户-角色 对应 list
        for (Object roleId : user.getRoles()) { // 遍历所有角色
            // 生成临时 UserRole 对象并添加到 userRoleList
            UserRole userRoleTmp = new UserRole();
            userRoleTmp.setUserId(user.getId());
            userRoleTmp.setRoleId(Long.valueOf(roleId.toString()));
            userRoleList.add(userRoleTmp);

            // 查找角色名称，添加到 用户-角色名称 list
            Role role = roleDao.selectById(Long.valueOf(roleId.toString()));
            userRoleNameList.add(role.getName());
        }
        iUserRoleService.saveBatch(userRoleList); // 批量新增 用户-角色 对应数据

        // 更新 redis 中用户角色信息
        String redisKey = "qap:login:userId:" + user.getId();
        LoginUser loginUser = (LoginUser) redisUtil.get(redisKey);
        if (loginUser != null) {
            loginUser.setPermissions(userRoleNameList);
            redisUtil.set(redisKey, loginUser);
        }

        return userUpdateRet;
    }

    @Override
    public int updateCurrentCompanyProject(User user) throws Exception {
        user.setUpdatedAt(null);
        user.setCreatedAt(null);
        int ret = userDao.updateById(user);

        // 更新用户 redis 缓存
        LoginUser loginUser = SecurityUtils.getCurrentLoginUser();
        loginUser.getUser().setCompanyId(user.getCompanyId());
        loginUser.getUser().setProjectId(user.getProjectId());
        String redisKey = "qap:login:userId:" + user.getId();
        redisUtil.set(redisKey, loginUser);

        return ret;
    }

    @Override
    public int locking(User user) throws Exception {
        User tmp = new User();
        tmp.setId(user.getId());
        tmp.setLocked(user.isLocked());

        // redis 删除用户缓存
        String redisKey = "qap:login:userId:" + user.getId();
        redisUtil.delete(redisKey);

        return userDao.updateById(tmp);
    }

    @Override
    public Boolean changeCode(User user) {
        User u = userDao.selectById(user.getId()); // 查询用户信息

        String hmacCode = HmacUtil.encrypt(u.getAccount() + user.getCode(), secretKey, HmacUtil.HMAC_MD5); // 生成code的md5
        u.setCode(hmacCode);
        u.setUpdatedAt(null);
        u.setCreatedAt(null);

        userDao.updateById(u); // 更新用户信息

        // redis 删除用户缓存
        String redisKey = "qap:login:userId:" + user.getId();
        redisUtil.delete(redisKey);

        return true;
    }

    @Override
    public ResponsePagingData list(MyPage<User> myPage) {
        Page<User> page = new Page(myPage.getPageNum(), myPage.getPageSize()); // 构造待查询 page 对象
        page.setOrders(myPage.getOrders()); // 设置排序字段

        // 构造待查询 QueryWrapper
        User user = myPage.getQuery();
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        if (user != null) {
            if (StringUtils.isNotBlank(user.getAccount())) {
                queryWrapper.like("account", user.getAccount());
            }
            if (StringUtils.isNotBlank(user.getName())) {
                queryWrapper.like("name", user.getName());
            }
        }
        queryWrapper.orderByDesc("id");

        page = userDao.selectPage(page, queryWrapper); // 按分页查询

        myPage.setTotalRecords(page.getTotal()); // 设置返回总记录数

        // 查询用户对应的角色
        for (User userTmp : page.getRecords()) {
            List<UserRole> userRoles = userRoleDao.selectByUserId(userTmp.getId());
            userTmp.setRoles(userRoles);
        }

        return new ResponsePagingData(myPage, page.getRecords());
    }

    @Override
    public ResponsePagingData listByProjectId(MyPage<User> myPage) {
        String column = "id";
        boolean asc = false;
        if (myPage.getOrders() != null && myPage.getOrders().size() != 0) {
            column = myPage.getOrders().get(0).getColumn();
            asc = myPage.getOrders().get(0).isAsc();
        }

        User user = myPage.getQuery();
        List<User> userList = userDao.listByProjectId(user.getProjectId(), user.getAccount(), user.getName(), column, asc, (myPage.getPageNum() -1L) * myPage.getPageSize(), myPage.getPageSize()); // 按分页查询

        myPage.setTotalRecords(userDao.countListByProjectId(user.getProjectId(), user.getAccount(), user.getName())); // 设置返回总记录数

        // 查询用户对应的角色
        for (User userTmp : userList) {
            List<UserRole> userRoles = userRoleDao.selectByUserId(userTmp.getId());
            userTmp.setRoles(userRoles);
        }

        return new ResponsePagingData(myPage, userList);
    }

    @Override
    public User selectWholeInfoById(Long id) {
        User user = userDao.selectWholeInfoById(id);

        List<Long> roleIds = new LinkedList<Long>();
        List<UserRole> userRoles = userRoleDao.selectByUserId(user.getId());
        for (UserRole userRole : userRoles) {
            roleIds.add(userRole.getRoleId());
        }
        user.setRoles(roleIds);

        return user;
    }

    @Override
    public List<User> selectUserByProjectId(Long id) {
        return userDao.selectUserByProjectId(id);
    }

    @Override
    public Object selectUserByNotIncludeProjectId(Long projectId) {
        return userDao.selectUserByNotIncludeProjectId(projectId);
    }

    @Override
    public Object generateToken(User user, Map<String, Object> extMap, int day) {
        String token = JWTUtils.generateToken(user, extMap, day);
        UserResponse userResponse = new UserResponse();
        userResponse.setToken(token);

        return userResponse;
    }

    @Override
    public Object removeUserProject(Long userId, Long projectId) {
        QueryWrapper<UserCompanyProject> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("project_id", projectId);
        queryWrapper.eq("user_id", userId);

        return userCompanyProjectDao.delete(queryWrapper);
    }
}
